home *** CD-ROM | disk | FTP | other *** search
/ Giga Games 1 / Giga Games.iso / net / hack / 3_1_3 / sys / vms / vmstty.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-01  |  14.0 KB  |  452 lines

  1. /*    SCCS Id: @(#)vmstty.c    3.1    93/06/27    */
  2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4. /* tty.c - (VMS) version */
  5.  
  6. #define NEED_VARARGS
  7. #include "hack.h"
  8. #include "wintty.h"
  9. #include "termcap.h"
  10.  
  11. #include <descrip.h>
  12. #include <iodef.h>
  13. #ifndef __GNUC__
  14. #include <smgdef.h>
  15. #include <ttdef.h>
  16. #include <tt2def.h>
  17. #else    /* values needed from missing include files */
  18. # define SMG$K_TRM_UP     274
  19. # define SMG$K_TRM_DOWN  275
  20. # define SMG$K_TRM_LEFT  276
  21. # define SMG$K_TRM_RIGHT 277
  22. # define TT$M_MECHTAB      0x00000100    /* hardware tab support */
  23. # define TT$M_MECHFORM      0x00080000    /* hardware form-feed support */
  24. # define TT$M_NOBRDCST      0x00020000    /* disable broadcast messages, but  */
  25. # define TT2$M_BRDCSTMBX  0x00000010    /* catch them in associated mailbox */
  26. # define TT2$M_APP_KEYPAD 0x00800000    /* application vs numeric keypad mode */
  27. #endif /* __GNUC__ */
  28. #ifdef USE_QIO_INPUT
  29. #include <ssdef.h>
  30. #endif
  31. #include <errno.h>
  32. #include <signal.h>
  33.  
  34. unsigned long LIB$DISABLE_CTRL(), LIB$ENABLE_CTRL();
  35. unsigned long SYS$ASSIGN(), SYS$DASSGN(), SYS$QIOW();
  36. #ifndef USE_QIO_INPUT
  37. unsigned long SMG$CREATE_VIRTUAL_KEYBOARD(), SMG$DELETE_VIRTUAL_KEYBOARD(),
  38.           SMG$READ_KEYSTROKE(), SMG$CANCEL_INPUT();
  39. #else
  40. static short FDECL(parse_function_key, (int));
  41. #endif
  42. static void NDECL(setctty);
  43. static void NDECL(resettty);
  44.  
  45. #define vms_ok(sts) ((sts)&1)
  46. #define META(c)  ((c)|0x80)    /* 8th bit */
  47. #define CTRL(c)  ((c)&0x1F)
  48. #define CMASK(c) (1<<CTRL(c))
  49. #define LIB$M_CLI_CTRLT CMASK('T')    /* 0x00100000 */
  50. #define LIB$M_CLI_CTRLY CMASK('Y')    /* 0x02000000 */
  51. #define ESC '\033'
  52. #define CSI META(ESC)        /* '\233' */
  53. #define SS3 META(CTRL('O'))    /* '\217' */
  54.  
  55. extern short ospeed;
  56. char erase_char, intr_char, kill_char;
  57. static boolean settty_needed = FALSE,  bombing = FALSE;
  58. static unsigned long kb = 0;
  59. #ifdef USE_QIO_INPUT
  60. static char inputbuf[15+1], *inp = 0;
  61. static int  inc = 0;
  62. #endif
  63.  
  64. #define QIO_FUNC    IO$_TTYREADALL|IO$M_NOECHO|IO$M_TRMNOECHO
  65. #ifdef MAIL
  66. #define TT_SPECIAL_HANDLING  (TT$M_MECHTAB|TT$M_MECHFORM|TT$M_NOBRDCST)
  67. #define TT2_SPECIAL_HANDLING (TT2$M_BRDCSTMBX)
  68. #else
  69. #define TT_SPECIAL_HANDLING  (TT$M_MECHTAB|TT$M_MECHFORM)
  70. #define TT2_SPECIAL_HANDLING (0)
  71. #endif
  72. #define Uword unsigned short
  73. #define Ubyte unsigned char
  74. struct _rd_iosb {        /* i/o status block for read */
  75.     Uword    status,  trm_offset;
  76.     Uword    terminator,  trm_siz;
  77. };
  78. struct _wr_iosb {        /* i/o status block for write */
  79.     Uword    status,  byte_cnt;
  80.     unsigned   : 32;
  81. };
  82. struct _sm_iosb {        /* i/o status block for sense-mode qio */
  83.     Uword      status;
  84.     Ubyte      xmt_speed,  rcv_speed;
  85.     Ubyte      cr_fill,  lf_fill,  parity;
  86.     unsigned   : 8;
  87. };
  88. struct _sm_bufr {        /* sense-mode characteristics buffer */
  89.     Ubyte      class,  type;        /* class==DC$_TERM, type==(various) */
  90.     Uword      buf_siz;        /* aka page width */
  91. #define page_width buf_siz        /* number of columns */
  92.           unsigned  tt_char    : 24;    /* primary characteristics */
  93.           unsigned  page_length    :  8;    /* number of lines */
  94.           unsigned  tt2_char    : 32;    /* secondary characteristics */
  95. };
  96. static struct {
  97.     struct _sm_iosb io;
  98.     struct _sm_bufr sm;
  99. } sg = {{0},{0}};
  100. static unsigned short tt_chan = 0;
  101. static unsigned long  tt_char_restore = 0, tt_char_active = 0,
  102.               tt2_char_restore = 0, tt2_char_active = 0;
  103. static unsigned long  ctrl_mask = 0;
  104.  
  105. int
  106. vms_getchar()
  107. {
  108.     short key;
  109.  
  110. #ifdef USE_QIO_INPUT
  111.     struct _rd_iosb iosb;
  112.     unsigned long sts;
  113.     unsigned char kb_buf;
  114.  
  115.     if (inc > 0) {
  116.     /* we have buffered character(s) from previous read */
  117.     kb_buf = *inp++;
  118.     --inc;
  119.     sts = SS$_NORMAL;
  120.     } else {
  121.     sts = SYS$QIOW(0, tt_chan, QIO_FUNC, &iosb, (void(*)())0, 0,
  122.                &kb_buf, sizeof kb_buf, 0, 0, 0, 0);
  123.     }
  124.     if (vms_ok(sts)) {
  125.     if (kb_buf == CTRL('C')) {
  126.         if (intr_char) gsignal(SIGINT);
  127.         key = (short)kb_buf;
  128.     } else if (kb_buf == '\r') {    /* <return> */
  129.         key = (short)'\n';
  130.     } else if (kb_buf == ESC || kb_buf == CSI || kb_buf == SS3) {
  131.         switch(parse_function_key((int)kb_buf)) {
  132.           case SMG$K_TRM_UP:    key = flags.num_pad ? '8' : 'k';  break;
  133.           case SMG$K_TRM_DOWN:  key = flags.num_pad ? '2' : 'j';  break;
  134.           case SMG$K_TRM_LEFT:  key = flags.num_pad ? '4' : 'h';  break;
  135.           case SMG$K_TRM_RIGHT: key = flags.num_pad ? '6' : 'l';  break;
  136.           default:            key = ESC;    break;
  137.         }
  138.     } else {
  139.         key = (short)kb_buf;
  140.     }
  141.     } else if (sts == SS$_HANGUP || iosb.status == SS$_HANGUP
  142.         || sts == SS$_DEVOFFLINE) {
  143.     gsignal(SIGHUP);
  144.     key = ESC;
  145.     } else            /*(this should never happen)*/
  146.     key = getchar();
  147.  
  148. #else   /*!USE_QIO_INPUT*/
  149.     static volatile int recurse = 0;    /* SMG is not AST re-entrant! */
  150.  
  151.     if (recurse++ == 0 && kb != 0) {
  152.     SMG$READ_KEYSTROKE(&kb, &key);
  153.     switch (key) {
  154.       case SMG$K_TRM_UP:    flags.num_pad ? '8' : key = 'k';  break;
  155.       case SMG$K_TRM_DOWN:    flags.num_pad ? '2' : key = 'j';  break;
  156.       case SMG$K_TRM_LEFT:    flags.num_pad ? '4' : key = 'h';  break;
  157.       case SMG$K_TRM_RIGHT: flags.num_pad ? '6' : key = 'l';  break;
  158.       case '\r':        key = '\n'; break;
  159.       default:        if (key > 255)    key = ESC;
  160.                 break;
  161.     }
  162.     } else {
  163.     /* abnormal input--either SMG didn't initialize properly or
  164.        vms_getchar() has been called recursively (via SIGINT handler).
  165.      */
  166.     if (kb != 0)            /* must have been a recursive call */
  167.         SMG$CANCEL_INPUT(&kb);    /*  from an interrupt handler       */
  168.     key = getchar();
  169.     }
  170.     --recurse;
  171. #endif    /* USE_QIO_INPUT */
  172.  
  173.     return (int)key;
  174. }
  175.  
  176. #ifdef USE_QIO_INPUT
  177.        /*
  178.     * We've just gotten an <escape> character.  Do a timed read to
  179.     * get any other characters, then try to parse them as an escape
  180.     * sequence.  This isn't perfect, since there's no guarantee
  181.     * that a full escape sequence will be available, or even if one
  182.     * is, it might actually by regular input from a fast typist or
  183.     * a stalled input connection.  {For packetized environments,
  184.     * cross plural(body_part(FINGER)) and hope for best. :-}
  185.     *
  186.     * This is needed to preserve compatability with SMG interface
  187.     * for two reasons:
  188.     *    1) retain support for arrow keys, and
  189.     *    2) treat other VTxxx function keys as <esc> for aborting
  190.     *    various NetHack prompts.
  191.     * The second reason is compelling; otherwise remaining chars of
  192.     * an escape sequence get treated as inappropriate user commands.
  193.     *
  194.     * SMG code values for these key sequences fall in the range of
  195.     * 256 thru 3xx.  The assignments are not particularly intuitive.
  196.     */
  197. /*=
  198.      -- Summary of VTxxx-style keyboards and transmitted escape sequences. --
  199. Keypad codes are prefixed by 7 bit (\033 O) or 8 bit SS3:
  200.     keypad:  PF1 PF2 PF3 PF4       codes:    P   Q    R   S
  201.           7   8   9   -            w   x    y   m
  202.           4   5   6   .            t   u    v   n
  203.           1   2   3  :en-:        q   r    s  : :
  204.          ...0...  ,  :ter:           ...p...    l  :M:
  205. Arrows are prefixed by either SS3 or CSI (either 7 or 8 bit), depending on
  206. whether the terminal is in application or numeric mode (ditto for PF keys):
  207.     arrows: <up> <dwn> <lft> <rgt>        A   B    D   C
  208. Additional function keys (vk201/vk401) generate CSI nn ~ (nn is 1 or 2 digits):
  209.     vk201 keys:  F6 F7 F8 F9 F10   F11 F12 F13 F14  Help Do   F17 F18 F19 F20
  210.    'nn' digits:  17 18 19 20 21    23  24  25  26    28  29   31  32  33  34
  211.      alternate:  ^C           ^[  ^H  ^J        (when in VT100 mode)
  212.    edit keypad: <fnd> <ins> <rmv>     digits:    1   2    3
  213.         <sel> <prv> <nxt>        4   5    6
  214. VT52 mode:  arrows and PF keys send ESCx where x is in A-D or P-S.
  215. =*/
  216.  
  217. static const char *arrow_or_PF = "ABCDPQRS",    /* suffix char */
  218.           *smg_keypad_codes = "PQRSpqrstuvwxyMmlnABDC";
  219.     /* PF1..PF4,KP0..KP9,enter,dash,comma,dot,up-arrow,down,left,right */
  220.     /* Ultimate return value is (index into smg_keypad_codes[] + 256). */
  221.  
  222. static short
  223. parse_function_key(c)
  224. register int c;
  225. {
  226.     struct _rd_iosb iosb;
  227.     unsigned long sts;
  228.     char seq_buf[15+1];        /* plenty room for escape sequence + slop */
  229.     short result = ESC;        /* translate to <escape> by default */
  230.  
  231.     /*
  232.      * Read whatever we can from type-ahead buffer (1 second timeout).
  233.      * If the user typed an actual <escape> to deliberately abort
  234.      * something, he or she should be able to tolerate the necessary
  235.      * restriction of a negligible pause before typing anything else.
  236.      * We might already have [at least some of] an escape sequence from a
  237.      * previous read, particularly if user holds down the arrow keys...
  238.      */
  239.     if (inc > 0) strncpy(seq_buf, inp, inc);
  240.     if (inc < sizeof seq_buf - 1) {
  241.     sts = SYS$QIOW(0, tt_chan, QIO_FUNC|IO$M_TIMED, &iosb, (void(*)())0, 0,
  242.                seq_buf + inc, sizeof seq_buf - 1 - inc, 1, 0, 0, 0);
  243.     if (vms_ok(sts))  sts = iosb.status;
  244.     } else
  245.     sts = SS$_NORMAL;
  246.     if (vms_ok(sts) || sts == SS$_TIMEOUT) {
  247.     register int cnt = iosb.trm_offset + iosb.trm_siz + inc;
  248.     register char *p = seq_buf;
  249.     if (c == ESC)    /* check for 7-bit vt100/ANSI, or vt52 */
  250.         if (*p == '[' || *p == 'O') c = META(CTRL(*p++)),  cnt--;
  251.         else if (strchr(arrow_or_PF, *p)) c = SS3; /*CSI*/
  252.     if (cnt > 0 && (c == SS3 || (c == CSI && strchr(arrow_or_PF, *p)))) {
  253.         register char *q = strchr(smg_keypad_codes, *p);
  254.         if (q) result = 256 + (q - smg_keypad_codes);
  255.         p++,  --cnt;    /* one more char consumed */
  256.     } else if (cnt > 1 && c == CSI) {
  257.         static short    /* "CSI nn ~" -> F_keys[nn] */
  258.         F_keys[35] = {    ESC,                /*(filler)*/
  259.                 311, 312, 313, 314, 315, 316,    /* E1-E6 */
  260.                 ESC, ESC, ESC, ESC,       /*(more filler)*/
  261.                 281, 282, 283, 284, 285, ESC,    /* F1-F5 */
  262.                 286, 287, 288, 289, 290, ESC,    /* F6-F10*/
  263.                 291, 292, 293, 294, ESC,    /*F11-F14*/
  264.                 295, 296, ESC, /*<help>,<do>, aka F15,F16*/
  265.                 297, 298, 299, 300        /*F17-F20*/
  266.         };  /* note: there are several missing nn in CSI nn ~ values */
  267.         int nn;  char *q;
  268.         *(p + cnt) = '\0';    /* terminate string */
  269.         q = strchr(p, '~');
  270.         if (q && sscanf(p, "%d~", &nn) == 1) {
  271.         if (nn > 0 && nn < SIZE(F_keys)) result = F_keys[nn];
  272.         cnt -= (++q - p);
  273.         p = q;
  274.         }
  275.     }
  276.     if (cnt > 0) strncpy((inp = inputbuf), p, (inc = cnt));
  277.     else         inc = 0,  inp = 0;
  278.     }
  279.     return result;
  280. }
  281. #endif    /* USE_QIO_INPUT */
  282.  
  283. static void
  284. setctty()
  285. {
  286.     struct _sm_iosb iosb;
  287.     unsigned long status;
  288.  
  289.     status = SYS$QIOW(0, tt_chan, IO$_SETMODE, &iosb, (void(*)())0, 0,
  290.               &sg.sm, sizeof sg.sm, 0, 0, 0, 0);
  291.     if (vms_ok(status))  status = iosb.status;
  292.     if (vms_ok(status)) {
  293.     /* try to force terminal into synch with TTDRIVER's setting */
  294.     number_pad((sg.sm.tt2_char & TT2$M_APP_KEYPAD) ? -1 : 1);
  295.     } else {
  296.     raw_print("");
  297.     errno = EVMSERR,  vaxc$errno = status;
  298.     perror("NetHack(setctty: setmode)");
  299.     wait_synch();
  300.     }
  301. }
  302.  
  303. static void
  304. resettty()            /* atexit() routine */
  305. {
  306.     if (settty_needed) {
  307.     bombing = TRUE;     /* don't clear screen; preserve traceback info */
  308.     settty((char *)NULL);
  309.     }
  310.     (void) SYS$DASSGN(tt_chan),  tt_chan = 0;
  311. }
  312.  
  313. /*
  314.  * Get initial state of terminal, set ospeed (for termcap routines)
  315.  * and switch off tab expansion if necessary.
  316.  * Called by init_nhwindows() and resume_nhwindows() in wintty.c
  317.  * (for initial startup and for returning from '!' or ^Z).
  318.  */
  319. void
  320. gettty()
  321. {
  322.     static $DESCRIPTOR(tty_dsc, "TT:");
  323.     int err = 0;
  324.     unsigned long status, zero = 0;
  325.  
  326.     if (tt_chan == 0) {        /* do this stuff once only */
  327.     flags.cbreak = OFF,  flags.echo = ON;    /* until setup is complete */
  328.     status = SYS$ASSIGN(&tty_dsc, &tt_chan, 0, 0);
  329.     if (!vms_ok(status)) {
  330.         raw_print(""),  err++;
  331.         errno = EVMSERR,  vaxc$errno = status;
  332.         perror("NetHack(gettty: $assign)");
  333.     }
  334.     atexit(resettty);   /* register an exit handler to reset things */
  335.     }
  336.     status = SYS$QIOW(0, tt_chan, IO$_SENSEMODE, &sg.io, (void(*)())0, 0,
  337.               &sg.sm, sizeof sg.sm, 0, 0, 0, 0);
  338.     if (vms_ok(status))  status = sg.io.status;
  339.     if (!vms_ok(status)) {
  340.     raw_print(""),  err++;
  341.     errno = EVMSERR,  vaxc$errno = status;
  342.     perror("NetHack(gettty: sensemode)");
  343.     }
  344.     ospeed = sg.io.xmt_speed;
  345.     erase_char = '\177';    /* <rubout>, aka <delete> */
  346.     kill_char = CTRL('U');
  347.     intr_char = CTRL('C');
  348.     (void) LIB$ENABLE_CTRL(&zero, &ctrl_mask);
  349.     /* Use the systems's values for lines and columns if it has any idea. */
  350.     if (sg.sm.page_length)
  351.     LI = sg.sm.page_length;
  352.     if (sg.sm.page_width)
  353.     CO = sg.sm.page_width;
  354.     /* suppress tab and form-feed expansion, in case termcap uses them */
  355.     tt_char_restore  = sg.sm.tt_char;
  356.     tt_char_active   = sg.sm.tt_char |= TT_SPECIAL_HANDLING;
  357.     tt2_char_restore = sg.sm.tt2_char;
  358.     tt2_char_active  = sg.sm.tt2_char |= TT2_SPECIAL_HANDLING;
  359. #if 0        /*[ defer until setftty() ]*/
  360.     setctty();
  361. #endif
  362.  
  363.     if (err) wait_synch();
  364. }
  365.  
  366. /* reset terminal to original state */
  367. void
  368. settty(s)
  369. const char *s;
  370. {
  371.     if (!bombing) end_screen();
  372.     if (s) raw_print(s);
  373.     disable_broadcast_trapping();
  374. #if 0        /* let SMG's exit handler do the cleanup (as per doc) */
  375. /* #ifndef USE_QIO_INPUT */
  376.     if (kb)  SMG$DELETE_VIRTUAL_KEYBOARD(&kb),  kb = 0;
  377. #endif    /* 0 (!USE_QIO_INPUT) */
  378.     if (ctrl_mask)
  379.         (void) LIB$ENABLE_CTRL(&ctrl_mask, 0);
  380.     flags.echo = ON;
  381.     flags.cbreak = OFF;
  382.     /* reset original tab, form-feed, broadcast settings */
  383.     sg.sm.tt_char  = tt_char_restore;
  384.     sg.sm.tt2_char = tt2_char_restore;
  385.     setctty();
  386.  
  387.     settty_needed = FALSE;
  388. }
  389.  
  390. /* same as settty, with no clearing of the screen */
  391. void
  392. shuttty(s)
  393. const char *s;
  394. {
  395.     bombing = TRUE;
  396.     settty(s);
  397.     bombing = FALSE;
  398. }
  399.  
  400. void
  401. setftty()
  402. {
  403.     unsigned long mask = LIB$M_CLI_CTRLT | LIB$M_CLI_CTRLY;
  404.  
  405.     (void) LIB$DISABLE_CTRL(&mask, 0);
  406.     if (kb == 0) {        /* do this stuff once only */
  407. #ifdef USE_QIO_INPUT
  408.         kb = tt_chan;
  409. #else   /*!USE_QIO_INPUT*/
  410.         SMG$CREATE_VIRTUAL_KEYBOARD(&kb);
  411. #endif  /*USE_QIO_INPUT*/
  412.         init_broadcast_trapping();
  413.     }
  414.     enable_broadcast_trapping();    /* no-op if !defined(MAIL) */
  415.     flags.cbreak = (kb != 0) ? ON : OFF;
  416.     flags.echo   = (kb != 0) ? OFF : ON;
  417.     /* disable tab & form-feed expansion; prepare for broadcast trapping */
  418.     sg.sm.tt_char  = tt_char_active;
  419.     sg.sm.tt2_char = tt2_char_active;
  420.     setctty();
  421.  
  422.     start_screen();
  423.     settty_needed = TRUE;
  424. }
  425.  
  426. void
  427. intron()        /* enable kbd interupts if enabled when game started */
  428. {
  429.     intr_char = CTRL('C');
  430. }
  431.  
  432. void
  433. introff()        /* disable kbd interrupts if required*/
  434. {
  435.     intr_char = 0;
  436. }
  437.  
  438.  
  439. /* fatal error */
  440. /*VARARGS1*/
  441. void
  442. error VA_DECL(const char *,s)
  443.     VA_START(s);
  444.     VA_INIT(s, const char *);
  445.     if(settty_needed)
  446.         settty(NULL);
  447.     Vprintf(s,VA_ARGS);
  448.     (void) putchar('\n');
  449.     VA_END();
  450.     exit(1);
  451. }
  452.